home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / newsgate / hdr.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-07-18  |  10.7 KB  |  506 lines

  1. /*
  2. **  Header-cracking and address re-writing routines, with sincere
  3. **  apologies to Upas and Sendmail.
  4. */
  5. #include "gate.h"
  6. #if    defined(DO_GETHOSTBYNAME)
  7. #include <netdb.h>
  8. #endif    /* defined(DO_GETHOSTBYNAME) */
  9. #if    defined(RCSID)
  10. static char RCS[] =
  11.     "$Header: /nfs/papaya/u2/rsalz/src/newsgate/RCS/hdr.c,v 1.21 91/07/18 20:00:57 rsalz Exp $";
  12. #endif    /* defined(RCSID) */
  13.  
  14.  
  15. #if    defined(TEST)
  16. #if    !defined(DO_FIX_ADDRESS)
  17. #define DO_FIX_ADDRESS
  18. #endif    /* !defined(DO_FIX_ADDRESS) */
  19. #if    !defined(DO_ADDRESS_CLEANUP)
  20. #define DO_ADDRESS_CLEANUP
  21. #endif    /* !defined(DO_ADDRESS_CLEANUP) */
  22. #else
  23. #define dprintf(string, buff)    /* NULL */
  24. #endif    /* defined(TEST) */
  25.  
  26.  
  27. #if    defined(DO_ADDRESS_CLEANUP)
  28. /*
  29. **  List of domains that we recognize.
  30. */
  31. STATIC char    *Domains[] = {
  32.     /* These aren't official domains, but we use them. */
  33.     ".BITNET",
  34.     ".UUCP",
  35.     /* Official organizational domains. */
  36.     ".ARPA",    ".COM",    ".EDU",    ".GOV",    ".INT",    ".MIL",
  37.     ".NATO",    ".NET",    ".ORG",
  38.     /* Official natonal domans. */
  39.     ".AR",    ".AT",    ".AU",    ".BE",    ".BR",    ".CA",    ".CH",    ".CL",
  40.     ".CN",    ".CR",    ".CS",    ".DE",    ".DK",    ".EC",    ".EG",    ".ES",
  41.     ".FI",    ".FR",    ".GR",    ".HK",    ".HU",    ".IE",    ".IL",    ".IN",
  42.     ".IS",    ".IT",    ".JP",    ".KR",    ".LK",    ".MX",    ".MY",    ".NI",
  43.     ".NL",    ".NO",    ".NZ",    ".PH",    ".PL",    ".PR",    ".PT",    ".SE",
  44.     ".SG",    ".SU",    ".TH",    ".TR",    ".TW",    ".UK",    ".US",    ".UY",
  45.     ".YU",    ".ZA"
  46. };
  47.  
  48.  
  49. /*
  50. **  Handle route-addresses:
  51. **    @cruft:joe@site --> joe@site
  52. */
  53. STATIC void
  54. RouteAddr(p)
  55.     register ADDRCHAR    *p;
  56. {
  57.     register ADDRCHAR    *q;
  58.     register ADDRCHAR    *r;
  59.  
  60.     if (*p != '@')
  61.     /* Not a route -- leave alone. */
  62.     return;
  63.  
  64.     /* Find the last '@'. */
  65.     for (r = p; *r; r++)
  66.     continue;
  67.     while (*--r != '@')
  68.     continue;
  69.  
  70.     /* Find the preceeding colon. */
  71.     for (q = r; q > p && *q != ':'; q--)
  72.     continue;
  73.     r = q++;
  74.     if (r > p)
  75.     while ((*p++ = *q++) != 0)
  76.         continue;
  77. }
  78.  
  79.  
  80. /*
  81. **  Handle the '%' syntax:
  82. **    joe%site.EDU@gateway.DOMAIN -> joe@site.EDU
  83. */
  84. STATIC void
  85. Percent(p)
  86.     register ADDRCHAR    *p;
  87. {
  88.     register ADDRCHAR    *q;
  89.     register ADDRCHAR    *r;
  90.     register ADDRCHAR    *s;
  91.     register ADDRCHAR    **dp;
  92.     register char    *pat;
  93.  
  94.     /* Find the rightmost '@'. */
  95.     for (r = p; *r; r++)
  96.     continue;
  97.     while (--r > p)
  98.     if (*r == '@')
  99.         break;
  100.  
  101.     for (; *r == '@'; r = q) {
  102.     /* Find the '%' just before the '@'. */
  103.     for (q = r; q > p && *q != '&'; q--)
  104.         continue;
  105.     if (*q != '%')
  106.         break;
  107.  
  108.     for (dp = Domains; dp < ENDOF(Domains); dp++) {
  109.         pat = *dp + strlen(*dp);
  110.         for (s = r, pat--, s--; pat > *dp && s > q; pat--, s--)
  111.         if (*s != *pat
  112.          && (!isupper(*pat) || *s != tolower(*pat)))
  113.             break;
  114.         if (*s != *pat)
  115.         /* Assume **dp == '.' or not letter. */
  116.         continue;
  117.         *r = '\0';
  118.         *q = '@';
  119.         break;
  120.     }
  121.     }
  122. }
  123.  
  124.  
  125. /*
  126. **  Handle hybrid "!" and "@" addresses:
  127. **    a!site!joe@site --> leave alone
  128. **    a!site!joe --> site!joe@a.uucp
  129. **    a!site.EDU!joe --> joe@site.EDU
  130. */
  131. STATIC void
  132. Hybrid(p)
  133.     register ADDRCHAR    *p;
  134. {
  135.     register ADDRCHAR    *q;
  136.     register ADDRCHAR    *r;
  137.     register ADDRCHAR    *user;
  138.  
  139.     /* Already has an '@' */
  140.     for (q = p; *q; q++)
  141.     if (*q == '@')
  142.         return;
  143.  
  144.     /* Make sure there is a '!' */
  145.     for (user = p; *user; user++)
  146.     if (*user == '!')
  147.         break;
  148.     if (*user != '!')
  149.     return;
  150.  
  151.     for (*q++ = '@', *user = '\0', r = p; *r; r++)
  152.     *q++ = *r;
  153.     /* Copy back to beginning of list */
  154.     for (*q = '\0', r = p; *++user; r++)
  155.     *r = *user;
  156.     for (*r = '\0', user = r; *user != '@'; user--)
  157.     if (*user == '.')
  158.         return;
  159.     *r++ = '.';
  160.     *r++ = 'U';
  161.     *r++ = 'U';
  162.     *r++ = 'C';
  163.     *r++ = 'P';
  164.     *r++ = '\0';
  165. }
  166. #endif    /* defined(DO_ADDRESS_CLEANUP) */
  167.  
  168.  
  169. #if    defined(TEST)
  170. STATIC void
  171. dprintf(prompt, p)
  172.     char    *prompt;
  173.     ADDRCHAR    *p;
  174. {
  175.     (void)printf("%s returns:  ", prompt);
  176.     for ( ; *p; p++) {
  177.     if (*p & QUOTE_MASK)
  178.         (void)putchar('\\');
  179.     (void)putchar(*p & UNQUOTE_MASK);
  180.     }
  181.     (void)putchar('\n');
  182. }
  183. #endif    /* defined(TEST) */
  184.  
  185. /*
  186. **  General address canonicalizer.
  187. */
  188. STATIC char *
  189. FixAddress(p)
  190.     register ADDRCHAR    *p;
  191. {
  192.     static char        buff[1024];
  193.     register char    *cp;
  194.     register int    local;
  195.     char        host[128];
  196.  
  197. #if    defined(DO_ADDRESS_CLEANUP)
  198.     RouteAddr(p);
  199.     dprintf("RouteAddr", p);
  200.     Percent(p);
  201.     dprintf("  Percent", p);
  202.     Hybrid(p);
  203.     dprintf("   Hybrid", p);
  204. #endif    /* defined(DO_ADDRESS_CLEANUP) */
  205.  
  206.     /* Copy address, removing all quoting, and see if it's local. */
  207.     for (local = 1, cp = buff; (*cp = *p & UNQUOTE_MASK) != '\0'; cp++, p++)
  208.     if (NETCHR(*p))
  209.         local = 0;
  210.  
  211.     if (local) {
  212.     if (gethostname(host, sizeof host) < 0) {
  213.         Fprintf(stderr, "%s:  Can't get my hostname, %s.\n",
  214.             Pname, strerror(errno));
  215.         exit(EX_TEMPFAIL);
  216.     }
  217.     Sprintf(cp, "%s@%s", p, host);
  218.     }
  219.     return buff;
  220. }
  221.  
  222.  
  223. /*
  224. **  This subroutine is a concession to the realities of the Internet and
  225. **  and the USENET. Much as the idea is distasteful and likely to get me
  226. **  in trouble, I have to hack message-ids into a format that the USENET
  227. **  won't choke on.  Pray that if we're doing multiple insertion point
  228. **  gatewaying that ALL the gateways mung exactly the same things.
  229. **
  230. **  (Death to HERMES! Death to UNIX/MM-11! Death to EAN!)
  231. */
  232. STATIC int
  233. FixMessageID(s)
  234.     register char    *s;
  235. {
  236.     register int    atdot;
  237.     register int    closed;
  238.  
  239.     /* Quickie tests -- why waste time? */
  240.     if (*s != '<')
  241.     return FALSE;
  242.  
  243.     for (atdot = FALSE, closed = FALSE; *++s; )
  244.     switch (*s) {
  245.     default:
  246.         if (!isascii(*s) || iscntrl(*s) || isspace(*s))
  247.         return FALSE;
  248.         break;
  249.     case '<':
  250.         /* Already got one. */
  251.         return FALSE;
  252.     case '>':
  253.         /* I hope no one is stupid enough to quote this... */
  254.         closed = TRUE;
  255.         s[1] = '\0';
  256.         break;
  257.     case '.':
  258.     case '@':
  259.         /* We should check for a domain spec, not just either/or. */
  260.         atdot = TRUE;
  261.         break;
  262.     case '\t':
  263.     case ' ':
  264.     case '/':
  265.     case '"':
  266.         /* Avoid various problem characters. */
  267.         *s = '_';
  268.         break;
  269.     }
  270.  
  271.     return atdot && closed;
  272. }
  273.  
  274.  
  275. /*
  276. **  Fix up the contents of In-Reply-To: fields and References: fields.
  277. */
  278. STATIC void
  279. FixReferences(hp)
  280.     register HBUF        *hp;
  281. {
  282.     register char        *cp;
  283.     register char        *ep;
  284.     register char        *p;
  285.     register char        *max;
  286.     char            scratch[LG_SIZE];
  287.  
  288.     cp = hp->followid;
  289.     max = cp + strlen(cp);
  290.     for (p = scratch; (cp = IDX(cp, '<')) != NULL; ) {
  291.     if ((ep = IDX(cp, '>')) == NULL
  292.      || ((ep - cp) + 1) > sizeof scratch - (p - scratch + 2))
  293.         /* Unterminated ID, or no more room. */
  294.         break;
  295.  
  296.     if (FixMessageID(cp)) {
  297.         if (p > scratch) {
  298.         *p++ = ' ';
  299.         *p++ = '\0';
  300.         }
  301.         p += APPEND(p, cp);
  302.     }
  303.     cp = ep + 2;
  304.     if (cp >= max)
  305.         break;
  306.     }
  307.     Strcpy(hp->followid, scratch);
  308. }
  309.  
  310.  
  311. /*
  312. **  Count the number of '@' in the string.
  313. */
  314. STATIC int
  315. AtCount(s)
  316.     register char    *s;
  317. {
  318.     register int    n;
  319.  
  320.     for (n = 0; *s; s++)
  321.     if (*s == '@')
  322.         n++;
  323.     return n;
  324. }
  325.  
  326.  
  327. /*
  328. **  Canonicalize the "From:" line into the form
  329. **    From: local-part@domain (full-name)
  330. ** RFC822 doesn't require the comment to be at the end of the string
  331. ** like that.
  332. */
  333. STATIC void
  334. FixFrom(hp)
  335.     register HBUF        *hp;
  336. {
  337.     register char        *p;
  338. #if    defined(DO_GETHOSTBYNAME)
  339.     register struct hostent    *host;
  340. #endif    /* defined(DO_GETHOSTBYNAME) */
  341.     char            address[LG_SIZE];
  342.     char            fullname[LG_SIZE];
  343.     char            scratch[sizeof address];
  344.     ADDRCHAR            temp[LG_SIZE];
  345.  
  346.     /* We should handle "Full-Name:" too, but it doesn't get read by the
  347.      * news header reader. */
  348.     (void)CrackFrom(temp, fullname, hp->from);
  349.     Strcpy(address, FixAddress(temp));
  350.  
  351.     if (AtCount(address) != 1)
  352.     p = NULL;
  353.     else {
  354.     p = RDX(address, '@');
  355.     *p++ = '\0';
  356.  
  357. #if    defined(DO_GETHOSTBYNAME)
  358.     /* If we can find the host's official name use that. */
  359.     if ((host = gethostbyname(p)) != NULL)
  360.         p = host->h_name;
  361. #endif    /* defined(DO_GETHOSTBYNAME) */
  362.  
  363.     /* We know have the canonical hostname; glue back together. */
  364.     Sprintf(scratch, "%s@%s", address, p);
  365.     Strncpy(address, scratch, sizeof address);
  366.     address[sizeof address - 1] = '\0';
  367.     p = IDX(address, '@');
  368.     *p++ = '\0';
  369.     }
  370.  
  371.     /* Policy decision; what to put in the path? */
  372. #if    defined(FIXED_PATH)
  373.     Strcpy(hp->path, FIXED_PATH);
  374. #else
  375. #if    defined(GATEWAY_NAME)
  376.     Sprintf(scratch, "%s!%s!%s", GATEWAY_NAME, p, address);
  377. #else
  378.     Sprintf(scratch, "%s!%s", p, address);
  379. #endif    /* defined(GATEWAY_NAME) */
  380.     Strncpy(hp->path, scratch, sizeof hp->path);
  381.     hp->path[sizeof hp->path - 1] = '\0';
  382. #endif    /* defined(FIXED_PATH) */
  383.  
  384.     /* Restore the @ if we took it out. */
  385.     if (p)
  386.     *--p = '@';
  387.  
  388.     if (fullname[0]) {
  389.     p = address + strlen(address);
  390.     *p++ = ' ';
  391.     *p++ = '(';
  392.     p += APPEND(p, fullname);
  393.     *p++ = ')';
  394.     *p++ = '\0';
  395.     }
  396.  
  397.     /* Stick the canonicalized From: back in. */
  398.     Strcpy(hp->from, address);
  399.     for (p = hp->from; *p; p++)
  400.     *p &= 0x7F;
  401. }
  402.  
  403.  
  404. #define ERROR "\
  405. Message-ID syntax error.\n\
  406. *** Please refer to page 23, paragraph 4.6.1. and Appendix D\n\
  407. *** of NIC RFC #822 for the correct syntax, and fix your mailer."
  408.  
  409. /*
  410. ** Check an RFC822 header for validity and hack it to RFC1036 spec.
  411. ** returns NULL for everything OK, or a character pointer to an
  412. ** error message.
  413. */
  414. char *
  415. HackHeader(hp, SubjectRequired)
  416.     register HBUF        *hp;
  417.     int                SubjectRequired;
  418. {
  419. #if    defined(REQUIRE_MESSAGE_ID)
  420.     /* Sendmail (almost) always has a Message-ID */
  421.     if (hp->ident[0] == '\0')
  422.     return "Message-ID header missing";
  423.     if (!FixMessageID(hp->ident))
  424.     return ERROR;
  425. #else
  426.     /* MMDF doesn't always have a Message-ID. */
  427.     if (hp->ident[0] && !FixMessageID(hp->ident))
  428.     return ERROR;
  429. #endif    /* defined(REQUIRE_MESSAGE_ID) */
  430.  
  431.     /* Newsgroups */
  432.     if (hp->nbuf[0] == '\0')
  433.     return "Newsgroups header missing";
  434.  
  435.     /* Subject */
  436.     if (hp->title[0] == '\0') {
  437.     if (SubjectRequired)
  438.         return "Subject header missing";
  439.     Strcpy(hp->title, "(none)");
  440.     }
  441.  
  442.     /* From */
  443.     if (hp->from[0] == '\0')
  444.     return "From header missing";
  445.     FixFrom(hp);
  446.  
  447.     /* References and In-Reply-To */
  448.     if (hp->followid[0]) 
  449.     FixReferences(hp);
  450.  
  451.     return NULL;
  452. }
  453.  
  454.  
  455. #if    defined(TEST)
  456.  
  457. char    *Pname = "address-test";
  458.  
  459. #if    !defined(HAVE_STRERROR)
  460. /*
  461. **  Return a printable representation of errno.
  462. */
  463. char *
  464. strerror(x)
  465.     int            x;
  466. {
  467.     static char        buff[20];
  468.  
  469.     if (x >= 0 && x < sys_nerr)
  470.     return sys_errlist[x];
  471.     Sprintf(buff, "Error code %d", x);
  472.     return buff;
  473. }
  474. #endif    /* !defined(HAVE_STRERROR) */
  475.  
  476. main()
  477. {
  478.     HBUF    hp;
  479.     char    buff[BUFSIZ];
  480.     int        interactive;
  481.  
  482.     interactive = isatty(0);
  483.     if (interactive)
  484.     (void)printf("Enter addresses, EOF to exit:\n");
  485.     for ( ; ; ) {
  486.     if (interactive) {
  487.         (void)printf(">  ");
  488.         (void)fflush(stdout);
  489.     }
  490.     if (gets(buff) == NULL)
  491.         break;
  492.     if (buff[0] != '#' && buff[0] != '\0') {
  493.         if (!interactive) {
  494.         (void)printf("%s\n", buff);
  495.         (void)fflush(stdout);
  496.         }
  497.         (void)strcpy(hp.from, buff);
  498.         FixFrom(&hp);
  499.         (void)printf("\t-> %s\n\n", hp.from);
  500.     }
  501.     }
  502.  
  503.     exit(0);
  504. }
  505. #endif    /* defined(TEST) */
  506.